home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-11-06 | 24.0 KB | 695 lines | [TEXT/MPCC] |
- /*************************************************************************
- ** **
- ** EE.C Expression Evaluator **
- ** **
- ** AUTHOR: Mark Morley **
- ** COPYRIGHT: (c) 1992 by Mark Morley **
- ** DATE: December 1991 **
- ** HISTORY: Jan 1992 - Made it squash all command line arguments **
- ** into one big long string. **
- ** - It now can set/get VMS symbols as if they **
- ** were variables. **
- ** - Changed max variable name length from 5 to 15 **
- ** Jun 1992 - Updated comments and docs **
- ** **
- ** RMD: Apr 1995 - Ported to Macintosh for CodeWarrior **
- ** - Semicolons may now termiante expressions. **
- ** This now allows several expressions to be **
- ** given at once. **
- ** - Changed function pointer definition for C++ **
- ** - Change int to short **
- ** - TYPE changed from double to float **
- ** - Fixed crash with undefined functions **
- ** - Reorganized as a class **
- ** - main() put into separate file (EEmain.cp) **
- ** **
- ** You are free to incorporate this code into your own works, even if it **
- ** is a commercial application. However, you may not charge anyone else **
- ** for the use of this code! If you intend to distribute your code, **
- ** I'd appreciate it if you left this message intact. I'd like to **
- ** receive credit wherever it is appropriate. Thanks! **
- ** **
- ** I don't promise that this code does what you think it does... **
- ** **
- ** Please mail any bug reports/fixes/enhancments to me at: **
- ** morley@camosun.bc.ca **
- ** or **
- ** Mark Morley **
- ** 3889 Mildred Street **
- ** Victoria, BC Canada **
- ** V8Z 7G1 **
- ** (604) 479-7861 **
- ** **
- ** For comments on the Mac port **
- ** Robert M. Douglas **
- ** McKellar Designs **
- ** rdouglas@mckellar.com or douglas@org.ecc.ubc.ca **
- ** http://www.mckellar.com/designs/ **
- ** **
- *************************************************************************/
-
- #include <stdlib.h>
- #include <unix.h>
- #include <string.h>
- #include <math.h>
-
- #include "CEquation.h"
-
- /*************************************************************************
- ** **
- ** VARIABLE DECLARATIONS **
- ** **
- *************************************************************************/
-
- /*
- Add any "constants" here... These are "read-only" values that are
- provided as a convienence to the user. Their values can not be
- permanently changed. The first field is the variable name, the second
- is its value.
- */
- VARIABLE CEquation::Consts[] =
- {
- /* name, value */
- { "pi", M_PI },
- { "e", M_E },
-
- { 0 }
- };
-
- /*
- Add any math functions that you wish to recognise here... The first
- field is the name of the function as it would appear in an expression.
- The second field tells how many arguments to expect. The third is
- a pointer to the actual function to use.
-
- FunctionPtr definition added by RMD
- */
- FUNCTION CEquation::Funcs[] =
- {
- /* name, funtion to call */
- { "sin", 1, (FunctionPtr)sin },
- { "cos", 1, (FunctionPtr)cos },
- { "tan", 1, (FunctionPtr)tan },
- { "asin", 1, (FunctionPtr)asin },
- { "acos", 1, (FunctionPtr)acos },
- { "atan", 1, (FunctionPtr)atan },
- { "sinh", 1, (FunctionPtr)sinh },
- { "cosh", 1, (FunctionPtr)cosh },
- { "tanh", 1, (FunctionPtr)tanh },
- { "exp", 1, (FunctionPtr)exp },
- { "log", 1, (FunctionPtr)log },
- { "log10", 1, (FunctionPtr)log10 },
- { "sqrt", 1, (FunctionPtr)sqrt },
- { "floor", 1, (FunctionPtr)floor },
- { "ceil", 1, (FunctionPtr)ceil },
- { "abs", 1, (FunctionPtr)fabs },
- // { "hypot", 2, (FunctionPtr)hypot },
- { "deg", 1, (FunctionPtr)deg },
- { "rad", 1, (FunctionPtr) rad },
-
- { "", 0, nil }
- };
-
-
-
- /*************************************************************************
- ** **
- ** Some custom math functions... Note that they must be prototyped **
- ** above (if your compiler requires it) **
- ** **
- ** deg( x ) Converts x radians to degrees. **
- ** rad( x ) Converts x degrees to radians. **
- ** **
- *************************************************************************/
-
- double
- deg( double x )
- {
- return( x * 180.0 / M_PI );
- }
-
- double
- rad( double x )
- {
- return( x * M_PI / 180.0 );
- }
-
-
- /*************************************************************************
- ** **
- ** Equation Evaluator constructor and destructor **
- ** **
- ** RMD April 14, 1995 **
- ** **
- ************************************************************************/
- CEquation::CEquation()
- {
- }
-
- CEquation::~CEquation()
- {
- }
-
- /*************************************************************************
- ** **
- ** GetSymbol( char* s ) **
- ** **
- ** This routine obtains a value from the program's environment. **
- ** This works for DOS and VMS (and other OS's???)
- ** **
- ************************************************************************/
-
- short CEquation::GetSymbol( char* s, TYPE* v )
- {
- char* e;
-
- e = getenv( s );
- if( !e )
- return( 0 );
- *v = atof( e );
- return( 1 );
- }
-
-
- /*************************************************************************
- ** **
- ** SetSymbol( char* s, char* v ) **
- ** **
- ** This VMS specific routine sets (or updates) a VMS symbol to a given **
- ** value **
- ** **
- *************************************************************************/
-
- #ifdef VAX
- SetSymbol( char* s, char* v )
- {
- struct dsc$descriptor_s sym;
- struct dsc$descriptor_s val;
- long typ = 1;
-
- sym.dsc$w_length = strlen( s );
- sym.dsc$a_pointer = s;
- sym.dsc$b_class = DSC$K_CLASS_S;
- sym.dsc$b_dtype = DSC$K_DTYPE_T;
- val.dsc$w_length = strlen( v );
- val.dsc$a_pointer = v;
- val.dsc$b_class = DSC$K_CLASS_S;
- val.dsc$b_dtype = DSC$K_DTYPE_T;
- return( LIB$SET_SYMBOL( &sym, &val, &typ ) );
- }
- #endif
-
-
- /*************************************************************************
- ** **
- ** strlwr( char* s ) Internal use only **
- ** **
- ** This routine converts a string to lowercase. I know many compilers **
- ** offer their own routine, but my VMS compiler didn't so... **
- ** Again, this one is ASCII specific! **
- ** **
- *************************************************************************/
-
- static void
- strlwr( char* s )
- {
- while( *s )
- {
- if( *s >= 'A' && *s <= 'Z' )
- *s += 32;
- s++;
- }
- }
-
-
- /*************************************************************************
- ** **
- ** ClearAllVars() **
- ** **
- ** Erases all user-defined variables from memory. Note that constants **
- ** can not be erased or modified in any way by the user. **
- ** **
- ** Returns nothing. **
- ** **
- *************************************************************************/
-
- void CEquation::ClearAllVars(void)
- {
- short i;
-
- for( i = 0; i < MAXVARS; i++ )
- {
- *mVars[i].name = 0;
- mVars[i].value = 0;
- }
- }
-
-
- /*************************************************************************
- ** **
- ** ClearVar( char* name ) **
- ** **
- ** Erases the user-defined variable that is called NAME from memory. **
- ** Note that constants are not affected. **
- ** **
- ** Returns 1 if the variable was found and erased, or 0 if it didn't **
- ** exist. **
- ** **
- *************************************************************************/
-
- Boolean CEquation::ClearVar( char* name )
- {
- short i;
-
- for( i = 0; i < MAXVARS; i++ )
- if( *mVars[i].name && ! strcmp( name, mVars[i].name ) )
- {
- *mVars[i].name = 0;
- mVars[i].value = 0;
- return true;
- }
- return false;
- }
-
-
- /*************************************************************************
- ** **
- ** GetValue( char* name, TYPE* value ) **
- ** **
- ** Looks up the specified variable (or constant) known as NAME and **
- ** returns its contents in VALUE. **
- ** **
- ** First the user-defined variables are searched, then the constants are **
- ** searched. **
- ** **
- ** Returns 1 if the value was found, or 0 if it wasn't. **
- ** **
- *************************************************************************/
-
- Boolean CEquation::GetValue( char* name, TYPE* value )
- {
- short i;
-
- /* First check for an environment variable reference... */
- if( *name == '_' )
- return( GetSymbol( name + 1, value ) );
-
- /* Now check the user-defined variables. */
- for( i = 0; i < MAXVARS; i++ )
- if( *mVars[i].name && ! strcmp( name, mVars[i].name ) )
- {
- *value = mVars[i].value;
- return true;
- }
-
- /* Now check the programmer-defined constants. */
- for( i = 0; *Consts[i].name; i++ )
- if( *Consts[i].name && ! strcmp( name, Consts[i].name ) )
- {
- *value = Consts[i].value;
- return true;
- }
- return false;
- }
-
-
- /*************************************************************************
- ** **
- ** SetValue( char* name, TYPE* value ) **
- ** **
- ** First, it erases any user-defined variable that is called NAME. Then **
- ** it creates a new variable called NAME and gives it the value VALUE. **
- ** **
- ** Returns 1 if the value was added, or 0 if there was no more room. **
- ** **
- *************************************************************************/
-
- Boolean CEquation::SetValue( char* name, TYPE* value )
- {
- char b[30];
- short i;
-
- #ifdef VAX
- if( *name == '_' )
- {
- sprintf( b, "%g", *value );
- if( SetSymbol( name + 1, b ) != SS$_NORMAL )
- return false;
- return true;
- }
- #endif
- ClearVar( name );
- for( i = 0; i < MAXVARS; i++ )
- if( ! *mVars[i].name )
- {
- strcpy( mVars[i].name, name );
- mVars[i].name[VARLEN] = 0;
- mVars[i].value = *value;
- return true;
- }
- return false;
- }
-
-
- /*************************************************************************
- ** **
- ** Parse() Internal use only **
- ** **
- ** This function is used to grab the next token from the expression that **
- ** is being evaluated. **
- ** **
- *************************************************************************/
-
- void CEquation::Parse(void)
- {
- char* t;
-
- mTokenType = 0;
- t = mToken;
- if (*mSource == ';') return;
- while( iswhite( *mSource ) )
- mSource++;
- if( isdelim( *mSource ) )
- {
- mTokenType = DEL;
- *t++ = *mSource++;
- }
- else if( isnumer( *mSource ) )
- {
- mTokenType = NUM;
- while( isnumer( *mSource ) )
- *t++ = *mSource++;
- }
- else if( isalpha( *mSource ) )
- {
- mTokenType = VAR;
- while( isalpha( *mSource ) )
- *t++ = *mSource++;
- mToken[VARLEN] = 0;
- }
- else if( *mSource )
- {
- *t++ = *mSource++;
- *t = 0;
- Throw( E_SYNTAX );
- }
- *t = 0;
- while( iswhite( *mSource ) )
- mSource++;
- }
-
-
- /*************************************************************************
- ** **
- ** Level1( TYPE* r ) Internal use only **
- ** **
- ** This function handles any variable assignment operations. **
- ** It returns a value of 1 if it is a top-level assignment operation, **
- ** otherwise it returns 0 **
- ** **
- *************************************************************************/
-
- Boolean CEquation::Level1( TYPE* r )
- {
- char t[VARLEN + 1];
-
- if( mTokenType == VAR )
- if( *mSource == '=' )
- {
- strcpy( t, mToken );
- Parse();
- Parse();
- if( !*mToken )
- {
- ClearVar( t );
- return(1);
- }
- Level2( r );
- if( ! SetValue( t, r ) )
- Throw( E_MAXVARS );
- return( 1 );
- }
- Level2( r );
- return( 0 );
- }
-
-
- /*************************************************************************
- ** **
- ** Level2( TYPE* r ) Internal use only **
- ** **
- ** This function handles any addition and subtraction operations. **
- ** **
- *************************************************************************/
-
- void CEquation::Level2( TYPE* r )
- {
- TYPE t = 0;
- char o;
-
- Level3( r );
- while( (o = *mToken) == '+' || o == '-' )
- {
- Parse();
- Level3( &t );
- if( o == '+' )
- *r = *r + t;
- else if( o == '-' )
- *r = *r - t;
- }
- }
-
-
- /*************************************************************************
- ** **
- ** Level3( TYPE* r ) Internal use only **
- ** **
- ** This function handles any multiplication, division, or modulo. **
- ** **
- *************************************************************************/
-
- void CEquation::Level3( TYPE* r )
- {
- TYPE t;
- char o;
-
- Level4( r );
- while( (o = *mToken) == '*' || o == '/' || o == '%' )
- {
- Parse();
- Level4( &t );
- if( o == '*' )
- *r = *r * t;
- else if( o == '/' )
- {
- if( t == 0 )
- Throw( E_DIVZERO );
- *r = *r / t;
- }
- else if( o == '%' )
- {
- if( t == 0 )
- Throw( E_DIVZERO );
- *r = fmod( *r, t );
- }
- }
- }
-
-
- /*************************************************************************
- ** **
- ** Level4( TYPE* r ) Internal use only **
- ** **
- ** This function handles any "to the power of" operations. **
- ** **
- *************************************************************************/
-
- void CEquation::Level4( TYPE* r )
- {
- TYPE t;
-
- Level5( r );
- if( *mToken == '^' )
- {
- Parse();
- Level5( &t );
- *r = pow( *r, t );
- }
- }
-
-
- /*************************************************************************
- ** **
- ** Level5( TYPE* r ) Internal use only **
- ** **
- ** This function handles any unary + or - signs. **
- ** **
- *************************************************************************/
-
- void CEquation::Level5( TYPE* r )
- {
- char o = 0;
-
- if( *mToken == '+' || *mToken == '-' )
- {
- o = *mToken;
- Parse();
- }
- Level6( r );
- if( o == '-' )
- *r = -*r;
- }
-
-
- /*************************************************************************
- ** **
- ** Level6( TYPE* r ) Internal use only **
- ** **
- ** This function handles any literal numbers, variables, or functions. **
- ** **
- *************************************************************************/
-
- void CEquation::Level6( TYPE* r )
- {
- short i;
- short n;
- TYPE a[3];
-
- if( *mToken == '(' )
- {
- Parse();
- if( *mToken == ')' )
- Throw( E_NOARG );
- Level1( r );
- if( *mToken != ')' )
- Throw( E_UNBALAN );
- Parse();
- }
- else
- {
- if( mTokenType == NUM )
- {
- *r = (TYPE) atof( mToken );
- Parse();
- }
- else if( mTokenType == VAR )
- {
- if( *mSource == '(' )
- {
- for( i = 0; Funcs[i].args; i++ )
- if( ! strcmp( mToken, Funcs[i].name ) )
- {
- Parse();
- n = 0;
- do
- {
- Parse();
- if( *mToken == ')' || *mToken == ',' )
- Throw( E_NOARG );
- a[n] = 0;
- Level1( &a[n] );
- n++;
- } while( n < 4 && *mToken == ',' );
- Parse();
- if( n != Funcs[i].args )
- {
- strcpy( mToken, Funcs[i].name );
- Throw( E_NUMARGS );
- }
- *r = Funcs[i].func( a[0], a[1], a[2] );
- return;
- }
- if( ! *Funcs[i].name )
- Throw( E_BADFUNC );
- }
- else if( ! GetValue( mToken, r ) )
- Throw( E_UNKNOWN );
- Parse();
- }
- else
- Throw( E_SYNTAX );
- }
- }
-
-
- /*************************************************************************
- ** **
- ** Evaluate( char* e, TYPE* result, short* a ) **
- ** **
- ** This function is called to evaluate the expression E and return the **
- ** answer in RESULT. If the expression was a top-level assignment, a **
- ** value of 1 will be returned in A, otherwise it will contain 0. **
- ** **
- ** Returns E_OK if the expression is valid, or an error code. **
- ** **
- *************************************************************************/
-
-
-
- Boolean CEquation::Evaluate( char* e, TYPE* result, short* a )
- {
- Try_ {
- mSource = e;
- mErrorFrom = mSource;
- while (*mSource != 0) {
- strlwr( mSource );
- *result = 0;
- Parse();
- if( ! *mToken ) Throw(E_EMPTY);
- *a = Level1( result );
- while ((*mSource == ';') || iswhite( *mSource ))
- mSource++;
- }
- }
- Catch_(err) {
- mErrorCode = err;
- return( err );
- } EndCatch_
- return E_OK;
- }
-
-
- /*************************************************************************
- ** **
- ** What follows is a main() routine that evaluates the command line **
- ** arguments one at a time, and displays the results of each expression. **
- ** Without arguments, it becomes an interactive calculator. **
- ** **
- *************************************************************************/
-
- #include <stdio.h>
-
- char* CEquation::ErrMsgs[] =
- {
- "Syntax error",
- "Unbalanced parenthesis",
- "Division by zero",
- "Unknown variable",
- "Maximum variables exceeded",
- "Unrecognised funtion",
- "Wrong number of arguments to funtion",
- "Missing an argument",
- "Empty expression"
- };
-
- void CEquation::ReportError(void)
- {
- printf( "ERROR: %s - %s", ErrMsgs[mErrorCode - 1], mToken );
- printf( "\n%s", mErrorFrom );
- printf( "\n%*s^\n", mSource - mErrorFrom - 1, "" );
- }
-
-
- void CEquation::ListVariables(void)
- {
- for(short i = 0; i < MAXVARS; i++ )
- if( *mVars[i].name )
- printf( "%s = %g\n", mVars[i].name, mVars[i].value );
- }
-
- void CEquation::ListConstants(void)
- {
- for(short i = 0; *Consts[i].name; i++ )
- printf( "%s = %g\n", Consts[i].name, Consts[i].value );
- }
-